package com.zegome.game.oanquan.gamelogic;

import java.util.ArrayList;
import java.util.Vector;

import com.zegome.game.oanquan.GameCenter;
import com.zegome.game.oanquan.scene.play.BoardManager;
import com.zegome.game.oanquan.scene.play.PondData;
import com.zegome.game.pool.GenericPool;

/**
 * (C) 2013 ZeroTeam
 * 
 * @author QuanLT
 */

public class AI {
	public static final int COMPUTER = 1;
	public static final int HUMAN = 0;
	private static final int MAX_VALUE = 100;

	private int mDepth;
	private int mCountNode = 0;

	public boolean isLogNode = false;
	public boolean isComplete = false;
	
	public Action mAction;
	final ArrayList<Node> mGameTree;

	final GenericPool<Node> mNodePool;
	
	public AI(final int depth) {
		mGameTree = new ArrayList<Node>();
		mDepth = depth;
		mAction = new Action();
		mNodePool = new GenericPool<Node>() {

			@Override
			public Node newObject() {
				return new Node(null, -1, null);
			}

			@Override
			public void onHandleRecycle(Node object) {
				object.mScores[0] = 0;
				object.mScores[1] = 0;
			}
		};
	}

	public void setDepth(final int depth) {
		mDepth = depth;
	}
	
	TempPond[] createTempPond(final PondData[] ponds) {
		final TempPond[] tempPond = new TempPond[BoardManager.TOTAL_POND];
		for (int i = 0; i < BoardManager.TOTAL_POND; i++) {
			tempPond[i] = new TempPond(i % 6 == 0);
			tempPond[i].deepCopy(ponds[i]);
		}
		return tempPond;
	}

	void copyInfo(final TempPond[] src, final TempPond[] target) {
		for (int i = 0; i < BoardManager.TOTAL_POND; i++) {
			target[i].deepCopy(src[i]);
		}
	}

	public void start(final PondData[] ponds, final int min, final int max) {
		if (mDepth == 0) {
			final Vector<Integer> index = new Vector<Integer>();
			for (int i = 7; i < 12; i++) {
				if (ponds[i].getStone() > 0) {
					final int k = i;
					index.add(k);
				}
			}
			mAction.mIndex = index.get(GameCenter.GAME_RANDOM.nextInt(index.size()));
			mAction.mDirection = (GameCenter.GAME_RANDOM.nextInt(2) == 0 ? BoardManager.LEFT : BoardManager.RIGHT);
			isComplete = true;
			return;
		}
		
		isComplete = false;
		mCountNode = 0;
		final TempPond[] tempPond = createTempPond(ponds);
		final Node temp = new Node(tempPond, COMPUTER, min, max);
		temp.mCurrentDepth = 0;

		alphaBeta(temp, -MAX_VALUE, MAX_VALUE);
	}

	private void alphaBeta(final Node temp, int alpha, int beta) {
		maxValue(temp, alpha, beta);
		isComplete = true;
		if (isLogNode) {
			System.out.println("mCountNode = " + mCountNode);
		}
	}

	private int maxValue(final Node temp, int alpha, int beta) {
//		System.out.println("a = "+alpha + " | b = "+beta);
		if (testTeminal(temp)) {
			return utility(temp);
		}

		int score = -MAX_VALUE;
		ArrayList<Node> successorNode = successor(temp);
		final int size = successorNode.size();

		for (int i = 0; i < size; i++) {
			final Node node = successorNode.get(i);
			final int value = minValue(node, alpha, beta);
			if (score < value) {
				if (node.mCurrentDepth == 1) {
					mAction.mIndex = node.mPreAction.mIndex;
					mAction.mDirection = node.mPreAction.mDirection;
				}
				score = value;
			}
			
			if (score >= beta) {
//				System.out.println("cut off");
				mNodePool.recycle(successorNode);
				successorNode.clear();
				successorNode = null;
				return score;
			}

			alpha = Math.max(alpha, score);
		}
		
		mNodePool.recycle(successorNode);
		successorNode.clear();
		successorNode = null;
		
		return score;
	}

	private int minValue(final Node temp, int alpha, int beta) {
//		System.out.println("a = "+alpha + " | b = "+beta);
		if (testTeminal(temp)) {
			return utility(temp);
		}

		int score = MAX_VALUE;
		ArrayList<Node> successorNode = successor(temp);
		final int size = successorNode.size();

		for (int i = 0; i < size ; i++) {
			final Node node = successorNode.get(i);
			final int value = maxValue(node, alpha, beta);
			if (score > value) {
				score = value;
			}

			if (score <= alpha) {
				mNodePool.recycle(successorNode);
				successorNode.clear();
				successorNode = null;
				return score;
			}

			beta = Math.min(beta, score);
		}
		
		mNodePool.recycle(successorNode);
		successorNode.clear();
		successorNode = null;
		
		return score;
	}

	private boolean testTeminal(final Node temp) {
		if (temp.mCurrentDepth == mDepth) {
			return true;
		}

		final TempPond[] ponds = temp.mTempPonds;

		if ((ponds[0].mValue == 0 && ponds[0].isEated)
				&& (ponds[6].mValue == 0 && ponds[6].isEated)) {
			for (int i = 1; i <= 5; i++) {
				if (ponds[i].mValue > 0) {
					temp.addScore(0, ponds[i].mValue);
					ponds[i].mValue = 0;
				}
			}

			for (int i = 7; i <= 11; i++) {
				if (ponds[i].mValue > 0) {
					temp.addScore(ponds[i].mValue, 0);
					ponds[i].mValue = 0;
				}
			}
			return true;
		}
		boolean isNeedAddStone = true;
		int factor = temp.mCurrentFlag == HUMAN ? 1 : 7;
		for (int i = 0; i < 5; i++) {
			if (ponds[i + factor].mValue > 0) {
				isNeedAddStone = false;
				break;
			}
		}

		if (isNeedAddStone) {
			if (temp.mScores[temp.mCurrentFlag] >= 5) {
				temp.mScores[temp.mCurrentFlag] -= 5;
				for (int j = 0; j < 5; j++) {
					ponds[j + factor].mValue++;
				}
			} else {
				return true;
			}
		}

		return false;
	}

	private int utility(final Node temp) {
//		System.out.println("value = " +temp.getValue());
		return temp.getValue();
	}

	private ArrayList<Node> successor(final Node temp) {
		final ArrayList<Node> evalues = new ArrayList<Node>();

		int factor = temp.mCurrentFlag == HUMAN ? 1 : 7;
		int flag = temp.mCurrentFlag == HUMAN ? COMPUTER : HUMAN;

		for (int i = 0; i < 5; i++) {
			final int index = i + factor;
			if (temp.mTempPonds[index].mValue > 0) {
				mCountNode += 2;
//				System.out.println("size = " + mNodePool.size());
//				System.out.println("value = " + temp.mTempPonds[index].mValue);
				final Node nodeLeft = mNodePool.obtain();
				nodeLeft.initArray(temp.mTempPonds, false);
				nodeLeft.mCurrentFlag = flag;
				nodeLeft.mScores[0] = temp.mScores[0];
				nodeLeft.mScores[1] = temp.mScores[1];
				nodeLeft.mCurrentDepth = temp.mCurrentDepth + 1;
				nodeLeft.mPreAction.mDirection = BoardManager.LEFT;
				nodeLeft.mPreAction.mIndex = index;

				devidePond(nodeLeft, index, BoardManager.LEFT,
						temp.mCurrentFlag);
				evalues.add(nodeLeft);

				final Node nodeRight = mNodePool.obtain();
				nodeRight.initArray(temp.mTempPonds, false);
				nodeRight.mCurrentFlag = flag;
				nodeRight.mScores[0] = temp.mScores[0];
				nodeRight.mScores[1] = temp.mScores[1];
				nodeRight.mPreAction.mDirection = BoardManager.RIGHT;
				nodeRight.mPreAction.mIndex = index;
				nodeRight.mCurrentDepth = temp.mCurrentDepth + 1;
				
				devidePond(nodeRight, index, BoardManager.RIGHT,
						temp.mCurrentFlag);
				
				evalues.add(nodeRight);
			}
		}

		return evalues;
	}

	private int nextIndex(int index, final int direction) {
		index += direction;
		return index = checkIndex(index);
	}

	private int checkIndex(int index) {
		if (index >= BoardManager.TOTAL_POND) {
			index -= BoardManager.TOTAL_POND;
		} else if (index < 0) {
			index += BoardManager.TOTAL_POND;
		}
		return index;
	}

	private void devidePond(final Node temp, final int index,
			final int direction, final int flag) {
		final TempPond[] ponds = temp.mTempPonds;
		final int count = ponds[index].mValue;
		if (count == 0)
			throw new IllegalArgumentException("loi AI devidePond");
		ponds[index].mValue = 0;
		int tempIndex = index;
		for (int i = 0; i < count; i++) {
			tempIndex = nextIndex(tempIndex, direction);
			ponds[tempIndex].mValue++;
		}
		tempIndex = nextIndex(tempIndex, direction);
		if (ponds[tempIndex].isCourting) {
			return;
		} else if (ponds[tempIndex].mValue == 0) {
			tempIndex = nextIndex(tempIndex, direction);
			checkEating(temp, tempIndex, direction, flag);
		} else {
			devidePond(temp, tempIndex, direction, flag);
		}
	}

	private void checkEating(final Node temp, final int index,
			final int direction, final int flag) {
		final TempPond[] ponds = temp.mTempPonds;
		final TempPond pond = ponds[index];

		int value;

		if (pond.mValue != 0 && pond.isEating()) {
			if (pond.isCourting && !pond.isEated) {
				pond.isEated = true;
			}

			value = pond.mValue;
			pond.mValue = 0;

			if (flag == HUMAN) {
				temp.addScore(0, value);
			} else {
				temp.addScore(value, 0);
			}

			int i = nextIndex(index, direction);
			if (ponds[i].mValue == 0) {
				i = nextIndex(i, direction);
				checkEating(temp, i, direction, flag);
			}

		}
	}
}
